﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.IdentityServer.ApiResources.Dtos;
using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.ObjectExtending;

namespace Volo.Abp.IdentityServer.ApiResources
{
	[Authorize(AbpIdentityServerPermissions.ApiResource.Default)]
	public class ApiResourceAppService : IdentityServerAppServiceBase, IRemoteService, IApplicationService, IApiResourceAppService
	{
		protected IApiResourceRepository ApiResourceRepository { get; }

		public ApiResourceAppService(IApiResourceRepository apiResourceRepository)
		{
			this.ApiResourceRepository = apiResourceRepository;
		}

		public virtual async Task<PagedResultDto<ApiResourceWithDetailsDto>> GetListAsync(GetApiResourceListInput input)
		{
			var resources = await this.ApiResourceRepository.GetListAsync(input.Sorting, input.SkipCount, input.MaxResultCount);
			var count = await this.ApiResourceRepository.GetCountAsync();
			List<ApiResourceWithDetailsDto> list = base.ObjectMapper.Map<List<ApiResource>, List<ApiResourceWithDetailsDto>>(resources);
			return new PagedResultDto<ApiResourceWithDetailsDto>(count, list);
		}

		public virtual async Task<List<ApiResourceWithDetailsDto>> GetAllListAsync()
		{
			var list = await this.ApiResourceRepository.GetListAsync();
			return base.ObjectMapper.Map<List<ApiResource>, List<ApiResourceWithDetailsDto>>(list);
		}

		public virtual async Task<ApiResourceWithDetailsDto> GetAsync(Guid id)
		{
			var apiResource = await this.ApiResourceRepository.GetAsync(id);
			return base.ObjectMapper.Map<ApiResource, ApiResourceWithDetailsDto>(apiResource);
		}

		[Authorize(AbpIdentityServerPermissions.ApiResource.Create)]
		public virtual async Task<ApiResourceWithDetailsDto> CreateAsync(CreateApiResourceDto input)
		{
			var flag = await this.ApiResourceRepository.CheckNameExistAsync(input.Name);
			if (flag)
			{
				throw new BusinessException("Volo.IdentityServer:DuplicateApiResourceName").WithData("Name", input.Name);
			}
			var apiResource = new ApiResource(base.GuidGenerator.Create(), input.Name, input.DisplayName, input.Description);
			string[] claims = input.Claims;
			for (int i = 0; i < claims.Length; i++)
			{
				apiResource.AddUserClaim(claims[i]);
			}
			HasExtraPropertiesObjectExtendingExtensions.MapExtraPropertiesTo<CreateApiResourceDto, ApiResource>(input, apiResource);
			var result = await this.ApiResourceRepository.InsertAsync(apiResource);
			return base.ObjectMapper.Map<ApiResource, ApiResourceWithDetailsDto>(result);
		}

		[Authorize(AbpIdentityServerPermissions.ApiResource.Update)]
		public virtual async Task<ApiResourceWithDetailsDto> UpdateAsync(Guid id, UpdateApiResourceDto input)
		{
			var apiResource = await this.ApiResourceRepository.GetAsync(id);
			apiResource.DisplayName = input.DisplayName;
			apiResource.Description = input.Description;
			apiResource.Enabled = input.Enabled;
			this.UpdateApiScope(input, apiResource);
			this.UpdateApiSecrets(input, apiResource);
			this.UpdateApiClaims(input, apiResource);
			HasExtraPropertiesObjectExtendingExtensions.MapExtraPropertiesTo<UpdateApiResourceDto, ApiResource>(input, apiResource);
			var result = await this.ApiResourceRepository.UpdateAsync(apiResource);
			return base.ObjectMapper.Map<ApiResource, ApiResourceWithDetailsDto>(result);
		}

		[Authorize(AbpIdentityServerPermissions.ApiResource.Delete)]
		public virtual async Task DeleteAsync(Guid id)
		{
			await this.ApiResourceRepository.DeleteAsync(id);
		}

		protected virtual void UpdateApiScope(UpdateApiResourceDto input, ApiResource apiResource)
		{
			foreach (ApiScopeDto apiScopeDto in input.Scopes)
			{
				ApiScope apiScope = apiResource.FindScope(apiScopeDto.Name);
				if (apiScope == null)
				{
					var apiScope2 = apiResource.AddScope(apiScopeDto.Name, apiScopeDto.DisplayName, apiScopeDto.Description, apiScopeDto.Required, apiScopeDto.Emphasize, apiScopeDto.ShowInDiscoveryDocument);
					if (apiScopeDto.UserClaims != null)
					{
						foreach (var apiScopeClaimDto in apiScopeDto.UserClaims)
						{
							apiScope2.AddUserClaim(apiScopeClaimDto.Type);
						}
					}
				}
				else
				{
					apiScope.DisplayName = apiScopeDto.DisplayName;
					apiScope.Description = apiScopeDto.Description;
					apiScope.Required = apiScopeDto.Required;
					apiScope.Emphasize = apiScopeDto.Emphasize;
					apiScope.ShowInDiscoveryDocument = apiScopeDto.ShowInDiscoveryDocument;
					this.UpdateApiScopeClaim(apiScopeDto, apiScope);
				}
			}
			var list = apiResource.Scopes.ToList<ApiScope>();
			foreach(var scope in list)
            {
				if (!input.Scopes.Any((ApiScopeDto c) => scope.Equals(apiResource.Id, c.Name)))
				{
					apiResource.RemoveScope(scope.Name);
				}
			}
		}

		protected virtual void UpdateApiScopeClaim(ApiScopeDto input, ApiScope apiScope)
		{
			if (input.UserClaims == null)
			{
				apiScope.RemoveAllUserClaims();
				return;
			}
			foreach (var apiScopeClaimDto in input.UserClaims)
			{
				if (apiScope.FindClaim(apiScopeClaimDto.Type) == null)
				{
					apiScope.AddUserClaim(apiScopeClaimDto.Type);
				}
			}
			var list = apiScope.UserClaims.ToList<ApiScopeClaim>();
			foreach(var claim in list)
            {
				if (!input.UserClaims.Any((ApiScopeClaimDto c) => claim.Equals(apiScope.ApiResourceId, apiScope.Name, c.Type)))
				{
					apiScope.RemoveClaim(claim.Type);
				}
			}
		}

		protected virtual void UpdateApiSecrets(UpdateApiResourceDto input, ApiResource apiResource)
		{
			foreach (var apiSecretDto in input.Secrets)
			{
				if (apiResource.FindSecret(apiSecretDto.Value, apiSecretDto.Type) == null)
				{
					apiSecretDto.Value = HashExtensions.Sha256(apiSecretDto.Value);
					apiResource.AddSecret(apiSecretDto.Value, apiSecretDto.Expiration, apiSecretDto.Type, apiSecretDto.Description);
				}
			}
			var list = apiResource.Secrets.ToList<ApiSecret>();
			foreach(var clientSecret in list)
            {
				if (input.Secrets.FirstOrDefault((ApiSecretDto s) => clientSecret.Equals(apiResource.Id, s.Value, s.Type)) == null)
				{
					apiResource.RemoveSecret(clientSecret.Value, clientSecret.Type);
				}
			}
		}

		protected virtual void UpdateApiClaims(UpdateApiResourceDto input, ApiResource apiResource)
		{
			foreach (string text in input.Claims)
			{
				if (apiResource.FindClaim(text) == null)
				{
					apiResource.AddUserClaim(text);
				}
			}
			var list = apiResource.UserClaims.ToList<ApiResourceClaim>();
			foreach(var claim in list)
            {
				if (input.Claims.FirstOrDefault((string c) => claim.Equals(apiResource.Id, c)) == null)
				{
					apiResource.RemoveClaim(claim.Type);
				}
			}
		}
	}
}
